import {
    Entity,
    EntityComponentTypes,
    EntityEquippableComponent,
    EquipmentSlot,
    GameMode,
    ItemStack,
    Player,
    system,
    TeleportOptions,
    Vector3,
    world,
} from "@minecraft/server";
import { LookDuration, register, SimulatedPlayer, Test } from "@minecraft/server-gametest";
import { Configdb, VectorUtils } from "sapi-pro";
import { calChunk, distance, intloc, Vector3Add } from "sapi-pro/scripts/func";
export const spTag = "simpleSPTag";

function isValid(test: Test | undefined): test is Test {
    return test != undefined;
}
export interface spConfig {
    structurePos: Vector3;
    spPrefix: string;
}
export interface spData {
    sp: SimulatedPlayer;
    acts: {
        follow?: { target: Player; interval: number; lastTick: number };
        jump?: { interval: number; lastTick: number };
        break?: { interval: number; lastTick: number };
        attack?: { target?: Entity; interval: number; lastTick: number };
    };
}

function isSpConfig(config: any): config is spConfig {
    return config != undefined && typeof config == "object" && config.structurePos && config.spPrefix;
}
export class SimulatedPlayerManager {
    spList: spData[];
    testObj: Test | undefined;
    Config: spConfig | undefined;
    constructor() {
        this.spList = [];
        this.testObj = undefined;
        world.afterEvents.worldLoad.subscribe(() => {
            const config = Configdb.getJSON("sp:config");
            if (isSpConfig(config)) this.Config = config;
        });
    }
    setConfig(config: spConfig) {
        if (!isSpConfig(config)) return false;
        const chunk = calChunk(config.structurePos);
        config.structurePos = {
            x: (chunk.min.x + chunk.max.x) / 2,
            y: config.structurePos.y,
            z: (chunk.min.z + chunk.max.z) / 2,
        };
        config.structurePos = intloc(config.structurePos);
        Configdb.setJSON("sp:config", config);
        this.Config = config;
        return true;
    }
    getConfig() {
        return this.Config;
    }
    registerTest() {
        if (!this.Config) return;
        register("yyj", "jr", async (test) => {
            this.testObj = test;
        })
            .structureLocation(this.Config.structurePos)
            .maxTicks(1200 * 60 * 24 * 30)
            .structureName("yyj:jr");
    }
    isLoaded() {
        return this.testObj != undefined;
    }
    getTest() {
        return this.testObj;
    }
    async initStructure() {
        if (this.testObj) return true;
        if (!this.Config) return;
        await new Promise((resolve) => {
            this.registerTest();
            system.runTimeout(() => {
                resolve(1);
            }, 5);
        });
        const dim = world.getDimension("overworld");
        dim.runCommand("gametest run yyj:jr");
        await new Promise(async (resolve) => {
            for (let i = 0; i < 5; i++) {
                await system.waitTicks(20);
                if (isValid(this.testObj)) break;
            }
            dim.runCommand("gamerule doMobSpawning true");
            dim.runCommand("gamerule doDaylightCycle true");
            dim.runCommand("gamerule randomTickSpeed 1");
            resolve(1);
        });
        return isValid(this.testObj);
    }
    Spawn(name: string, pos: Vector3, options: TeleportOptions) {
        if (this.Config == undefined || !isValid(this.testObj)) return "假人未初始化";
        const test = this.testObj;
        if (this.spList.length >= 20) {
            return "假人太多了";
        }
        const sp = test.spawnSimulatedPlayer(
            this.toRelativeLoc(this.Config!.structurePos, pos),
            this.Config!.spPrefix + name,
            GameMode.Survival
        );
        sp.teleport(pos, options as any);
        sp.addTag(spTag);
        this.spList.push({ sp: sp, acts: {} });
        return sp;
    }
    Remove(player: Player | SimulatedPlayer) {
        if (!isValid(this.testObj)) return;
        const index = this.spList.findIndex((t) => t.sp.id == player.id);
        if (index != -1) {
            const temp = this.spList[index].sp;
            this.spList.splice(index, 1);
            this.testObj.removeSimulatedPlayer(temp);
        }
    }
    /*设置假人看向的方向 */
    setLookAt(player: Player, sp: SimulatedPlayer) {
        const direction = player.getViewDirection();
        sp.lookAtLocation(
            this.toRelativeLoc(
                this.Config!.structurePos,
                Vector3Add(VectorUtils.scale(direction, 1000), sp.getHeadLocation())
            )
        );
    }
    useItem(sp: SimulatedPlayer, slot: number) {
        sp.useItemInSlot(slot);
    }
    useItemOnBlock(sp: SimulatedPlayer, slot: number) {
        const pos = sp.getBlockFromViewDirection()?.block.location;
        if (pos) {
            sp.useItemInSlotOnBlock(slot, this.toRelativeLoc(this.Config!.structurePos, pos));
        }
    }
    getInv(sp: SimulatedPlayer) {
        const container = sp.getComponent(EntityComponentTypes.Inventory)?.container;
        if (!container) return;
        const items: spItems = [];
        for (let i = 0; i < container.size; i++) {
            const item = container.getItem(i);
            if (item) {
                items.push({ name: (item as ItemStack).localizationKey, slot: i });
            }
        }
        return items;
    }
    getHandItem(sp: SimulatedPlayer) {
        const equip = sp.getComponent(EntityComponentTypes.Equippable);
        if (equip) {
            const item = (equip as EntityEquippableComponent).getEquipment(EquipmentSlot.Mainhand);
            return item;
        }
    }
    getSPList() {
        return this.spList;
    }
    getSPDatabyName(name: string) {
        let name1 = name.match(/@?("([^"]*)"|([^\s]+))/);
        if (!name1) return;
        name = name1[2] ? name1[2] : name1[3];
        return this.spList.find((t) => t.sp.name === name || t.sp.name.replace(this.Config?.spPrefix!, "") == name);
    }
    mes(player: Player, text: string) {
        player.sendMessage("§4[假人]§r" + text);
    }
    toRelativeLoc(structureLoc: Vector3, loc: Vector3) {
        return {
            x: loc.x - structureLoc.x,
            y: loc.y - structureLoc.y,
            z: loc.z - structureLoc.z,
        };
    }
    isSimulatedPlayer(playerName: string) {
        if (!this.Config) return false;
        return playerName.startsWith(this.Config!.spPrefix);
    }
}

// 定义基础动作接口
interface BaseAction {
    interval: number;
    lastTick: number;
}

// 定义完整动作配置
const Acts = {
    jump: {
        default: { interval: 20 },
        setter(spData: spData, config: any) {
            spData.acts.jump = {
                ...this.default,
                lastTick: 0,
            };
        },
        execute(sp: SimulatedPlayer, action: BaseAction) {
            sp.jump();
        },
        dispose(sp: SimulatedPlayer) {},
    },
    break: {
        default: { interval: 10 },
        setter(spData: spData) {
            spData.acts.break = {
                ...this.default,
                lastTick: 0,
            };
        },
        execute(sp: SimulatedPlayer, action: BaseAction) {
            const hitBlock = sp.getBlockFromViewDirection();
            const pos = hitBlock?.block.center();
            if (pos && distance(pos, sp.location) <= 27.5) {
                sp.breakBlock(spManager.toRelativeLoc(spManager.Config!.structurePos, pos));
            } else {
                sp.stopBreakingBlock();
            }
        },
        dispose(sp: SimulatedPlayer) {
            sp.stopBreakingBlock();
        },
    },
    follow: {
        default: { interval: 10 },
        setter(spData: spData, target: Player) {
            spData.acts.follow = {
                ...this.default,
                target,
                lastTick: 0,
            };
        },
        execute(sp: SimulatedPlayer, action: BaseAction & { target: Player }) {
            if (!action.target.isValid) return;
            const dis = distance(sp.location, action.target.location);
            if (dis > 16) {
                sp.navigateToEntity(action.target as any);
            }
        },
        dispose(sp: SimulatedPlayer) {},
    },
    attack: {
        default: { interval: 30 },
        setter(spData: spData, target?: Entity) {
            spData.acts.attack = {
                ...this.default,
                target,
                lastTick: 0,
            };
        },
        execute(sp: SimulatedPlayer, action: BaseAction & { target?: Entity }) {
            const target = action.target?.isValid
                ? action.target
                : sp.getEntitiesFromViewDirection().find((e) => e instanceof Entity);
            target && sp.attack();
        },
        dispose(sp: SimulatedPlayer) {},
    },
};

class SimulatedPlayerAction {
    spList: spData[];
    manager: SimulatedPlayerManager;
    constructor(manager: SimulatedPlayerManager) {
        this.spList = manager.spList;
        this.manager = manager;
    }
    call<T extends keyof typeof Acts>(name: string, actionType: T, config?: Parameters<(typeof Acts)[T]["setter"]>[1]) {
        const spData = this.manager.getSPDatabyName(name);
        if (!spData) return "未找到假人";
        const action = Acts[actionType];
        // 切换动作状态
        if (spData.acts[actionType]) {
            Acts[actionType as keyof typeof Acts].dispose(spData.sp);
            delete spData.acts[actionType];
            return `已停止 ${actionType}`;
        } else {
            // 使用类型断言解决参数传递问题
            (action.setter as any)(spData, config);
            return `已开始 ${actionType}`;
        }
    }
    loop() {
        const currentTick = system.currentTick;
        this.spList.forEach((spData) => {
            const { sp, acts } = spData;
            Object.entries(acts).forEach(([actionType, action]) => {
                const config = Acts[actionType as keyof typeof Acts];
                if (!config || currentTick - action.lastTick < action.interval) return;
                // 执行动作
                config.execute(sp, action as any);
                action.lastTick = currentTick;
                // 自动清理无效目标
                if (actionType === "follow" && !(action as any).target.isValid) {
                    delete acts[actionType];
                }
            });
        });
    }
}

export const spManager = new SimulatedPlayerManager();
export const SPAction = new SimulatedPlayerAction(spManager);
